﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Net.Mail;
using System.Data;
using System.IO;

/// <summary>
/// Summary description for BasicFuncs
/// </summary>
public static class BasicFuncs
{

    /// <summary>
    /// Checks to see if a specified email address is valid
    /// </summary>
    /// <param name="email"></param>
    /// <returns></returns>
    public static bool IsValidEmail(string email)
    {
        try
        {
            var addr = new System.Net.Mail.MailAddress(email);
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Returns the MVC url for the specified action and controller.  Allows webapi calls to generate mvc urls.
    /// Returns an empty string if the controller specified is "None"
    /// </summary>
    /// <param name="ActionName"></param>
    /// <param name="ControllerName"></param>
    /// <returns></returns>
    public static string GetMVCLink(string ActionName,string ControllerName)
    {
        UrlHelper myurlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
        if(ControllerName == "None")
        {
            return "";
        }
        return myurlHelper.Action(ActionName, ControllerName);
    }

    /// <summary>
    /// Retrieves the PersonID if given the username from a call to User.Identity.Name
    /// </summary>
    /// <param name="username"></param>
    /// <returns></returns>
    public static string GetPersonIDFromUsername(string username)
    {
        // this retrieves the user id from the database
        Dictionary<string, string> Parameters = new Dictionary<string, string>();
        Parameters.Add("JSON", "{\"@P1\":\"" + username + "\"}");
        DataTable MyUser = DataFuncs.ReadDataFromDB(0,"Security_Logon", Parameters);
        if (MyUser.Rows.Count == 0)
        {
            return "-1";
        }
        else
        {
            return MyUser.Rows[0]["EntityID"].ToString(); ;
        }
    }

    /// <summary>
    /// Retrieves the full name if given the username from a call to User.Identity.Name
    /// </summary>
    /// <param name="username"></param>
    /// <returns></returns>
    public static string GetNameFromUsername(string username)
    {
        // this retrieves the user id from the database
        Dictionary<string, string> Parameters = new Dictionary<string, string>();
        Parameters.Add("JSON", "{\"@P1\":\"" + username + "\"}");
        DataTable MyUser = DataFuncs.ReadDataFromDB(0,"Security_Logon", Parameters);
        if (MyUser.Rows.Count == 0)
        {
            return "-1";
        }
        else
        {
            return MyUser.Rows[0]["FullName"].ToString(); ;
        }
    }

    /// <summary>
    /// Sends emails based on various criteria.  More detail given in the variable descriptions.
    /// </summary>
    /// <param name="Subject">The subject of the email.  Any column in the datatable can be specified for replacement by using #columnname#</param>
    /// <param name="MessageBody">The message body of the email.  Any column in the datatable can be specified for replacement by using #columnname#</param>
    /// <param name="EmailData">DataTable that must contain an EntityID column in order to work. 
    ///                         A Category column will specify which category of email address will be used. 
    ///                         An Email column will specify that the email should only be sent to that address.
    ///                         Other columns can be used to replace entries in the body or subject of the email.
    ///                         If there is a column named "Attachments" the system will assume it is a comma-separated list of 
    ///                         file paths that should be attached to the email.</param>
    /// <param name="From">The email address the messages will be sent from. Uses the appsetting EmailSender if empty.</param>
    /// <param name="TestRun">Specifying true tests all entityid/category pairs to make sure they have a valid email address.
    ///                       It also builds the email message with any attachments.
    ///                       It does not actually send any emails.</param>
    /// <returns>An error report listing EntityIDs and the error experienced.</returns>
    public static string SendEmail(string Subject, string MessageBody, DataTable EmailData, 
                                   string From = "", bool TestRun = false) 
    {
        string EntityIDList = "";
        string EmailAddressList = "";
        string ErrorReport = "";
        string SendingMessage = "";
        string ReplaceSubject = "";
        string ReplaceMessageBody = "";
        string TestOutput = "";
        int TestCount = 0;
        int SendCount = 0;
        
        // Check to see if DataTable contains a Category or EntityID column
        if(!EmailData.Columns.Contains("EntityID"))
        {
            throw new ArgumentException("DataTable does not contain EntityID");
        }

        // Build a string of entity ids to use for retrieving the email addresses
        EntityIDList = String.Join(",", Enumerable.Select(EmailData.AsEnumerable(), id => id["EntityID"].ToString()).ToArray<string>());

        // this gets a table of email addresses for the entity ids
        DataTable ValidEmails = DataFuncs.GetAnyTableCoreData("RS_Email_With_Usage","EntityID IN (" + EntityIDList + ")");

        foreach(DataRow TempRow in EmailData.Rows)
        {
            if(EmailData.Columns.Contains("Email"))
            {
                EmailAddressList = TempRow["Email"].ToString();
            }
            else
            {
                // Collects the Email column returned by ValidEmails and joins them as a comma-separated list
                if (EmailData.Columns.Contains("Category"))
                {
                    EmailAddressList = String.Join(";", Enumerable.Select(ValidEmails.Select("EntityID = " + TempRow["EntityID"].ToString() + " AND Category = '" + TempRow["Category"].ToString() + "'").AsEnumerable(),
                                                        email => email["Email"].ToString()).ToArray<string>());
                }
                else
                {
                    EmailAddressList = String.Join(";", Enumerable.Select(ValidEmails.Select("EntityID = " + TempRow["EntityID"].ToString()).AsEnumerable(),
                                                        email => email["Email"].ToString()).Distinct<string>().ToArray<string>());
                }
            }
            // There were no email addresses returned.
            if (EmailAddressList == "")
            {
                ErrorReport += "There is no email address specified for the EntityID " + TempRow["EntityID"].ToString();
                if (EmailData.Columns.Contains("Category"))
                {
                    ErrorReport += " and the category " + TempRow["Category"].ToString();
                }
                ErrorReport += Environment.NewLine;
            }
            else
            {
                ReplaceSubject = Subject;
                ReplaceMessageBody = MessageBody;
                // Replace #columnname# variables in subject and messagebody with the values from the datarow
                foreach (string ColName in EmailData.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray())
                {
                    ReplaceSubject = ReplaceSubject.Replace("#" + ColName + "#", TempRow[ColName].ToString());
                    ReplaceMessageBody = ReplaceMessageBody.Replace("#" + ColName + "#", TempRow[ColName].ToString());
                }
                
                if(TestCount < 3 && TestRun)
                { 
                    // Testrun should actually return useful output:
                    if (TestOutput != "")
                        TestOutput += "|";
                    TestOutput += "<p>To: " + EmailAddressList + "</p>";
                    TestOutput += "<h1>" + ReplaceSubject + "</h1>";
                    TestOutput += ReplaceMessageBody;
                    TestCount++;
                }

                // if an attachment is specified, attatch it to the email.
                foreach (string email in EmailAddressList.Split(';'))
                {
                    if (BasicFuncs.IsValidEmail(email))
                    {
                        if (EmailData.Columns.Contains("Attachments"))
                        {
                            try
                            {
                                foreach(string AttachmentName in TempRow["Attachments"].ToString().Split(','))
                                {
                                    FileStream fileToRead = new FileStream(HttpContext.Current.Server.MapPath(
                                        "~/" + AttachmentName),FileMode.Open,FileAccess.Read);
                                    fileToRead.Close();
                                }
                            }
                            catch (Exception ex)
                            {
                                ErrorReport += "The following error occurred when sending to the email address " + email
                                                + " for EntityID " + TempRow["EntityID"].ToString();
                                ErrorReport += ": " + ex.Message + Environment.NewLine;
                            }
                        }

                        // Count the number of emails that will be sent.
                        SendCount ++;
                        if (TestRun == false)
                        {
                            if (EmailData.Columns.Contains("Attachments"))
                            {
                                SendingMessage = SendEmail(ReplaceSubject, ReplaceMessageBody, email,From,TempRow["Attachments"].ToString());
                            }
                            else
                            {
                                SendingMessage = SendEmail(ReplaceSubject, ReplaceMessageBody, email,From);
                            }
                            // if the email was not sent successfully
                            if (SendingMessage != "Email sent successfully.")
                            {
                                ErrorReport += "The following error occurred when sending to the email address " + email
                                                + " for EntityID " + TempRow["EntityID"].ToString();
                                ErrorReport += ": " + SendingMessage + Environment.NewLine;
                            }
                        }
                    }
                    else
                    {
                        ErrorReport += "The email address " + email + " specified for EntityID " + TempRow["EntityID"].ToString();
                        if (EmailData.Columns.Contains("Category"))
                        {
                            ErrorReport += " and the category " + TempRow["Category"].ToString();
                        }
                        ErrorReport += " is invalid." + Environment.NewLine;
                    }
                }
            }
        }
        if(TestRun)
        {
            ErrorReport += SendCount + " email addresses are valid and ready for sending.";
            ErrorReport += "<br />" + TestOutput;
        }
        else
        {
            ErrorReport += SendCount + " emails sent successfully.";
        } 
        return ErrorReport;
    }

    /// <summary>
    /// Behind the scenes controller that actually sends the emails. 
    /// </summary>
    /// <param name="Subject">The subject of the email</param>
    /// <param name="Body">The body of the email</param>
    /// <param name="Addresses">A semicolon-separated list of addresses to email</param>
    /// <param name="From">The email address to send from.</param>
    /// <param name="Attachments">A comma-separated list of attachments (full file path) to attach</param>
    /// <returns></returns>
    public static string SendEmail(string Subject,string Body,string Addresses,string From = "",string Attachments = "")
    {
        SmtpClient smtp;

        foreach(string address in Addresses.Split(';'))
        {
            MailMessage mm;

            //(1) Create the MailMessage instance
            if(IsValidEmail(From))
            {
                mm = new MailMessage(From, address);
            }
            else
            {
                mm = new MailMessage(System.Configuration.ConfigurationManager.AppSettings["EmailSender"], address);
            }

            //(2) Assign the MailMessage's properties
            mm.Subject = Subject;
            mm.Body = Body;
            mm.IsBodyHtml = true;
            if(Attachments != "")
            {
                foreach (string AttachmentName in Attachments.Split(','))
                {
                    mm.Attachments.Add(new Attachment(HttpContext.Current.Server.MapPath(
                                            "~/" + AttachmentName)));
                }
            }

            //(3) Create the SmtpClient object
            smtp = new SmtpClient();
            smtp.Credentials = new System.Net.NetworkCredential(
                System.Configuration.ConfigurationManager.AppSettings["EmailUsername"], 
                System.Configuration.ConfigurationManager.AppSettings["EmailPassword"]);

            try
            {
                //(4) Send the MailMessage (will use the Web.config settings)
                smtp.Send(mm);

            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
        return "Email sent successfully.";
    }

    /// <summary>
    /// Creates a random password of length characters long that contains at least one upper-case letter, lower-case letter and number.
    /// </summary>
    /// <param name="length"></param>
    /// <returns></returns>
    public static string CreatePassword(int length)
    {
        const string valid = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789";
        String res = "";
        int pwLength = length;
        Random rnd = new Random();
        // if the password doesn't have an upper-case letter, a lower-case letter and a number, start over.
        while (-1 == res.IndexOfAny(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' })
            || -1 == res.IndexOfAny(new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' })
            || -1 == res.IndexOfAny(new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }))
        {
            res = "";
            pwLength = length;
            while (0 < pwLength--)
            {
                res += valid[rnd.Next(valid.Length)];
            }
        }
        return res.ToString();
    }

    /// <summary>
    /// Use this to generate something similar to KENDO's ListView based on a template
    /// </summary>
    /// <param name="HTMLTemplate">Pass valid HTML code here which will be repeated for as many rows as exist in the dataset. Include column names inside # #</param>
    /// <param name="ListData">Pass any table of data with column names which are used to replace values in the template</param>
    /// <returns>Returns a string with all data replaced</returns>
    public static string HTMLListTemplate(string HTMLTemplate, DataTable ListData)
    {
        string completeHTML = "";
        string ReplaceHTMLTemplate = "";
        foreach (DataRow TempRow in ListData.Rows)
        {
            ReplaceHTMLTemplate = HTMLTemplate;
            // Replace #columnname# variables in subject and messagebody with the values from the datarow
            foreach (string ColName in ListData.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray())
            {
                ReplaceHTMLTemplate = ReplaceHTMLTemplate.Replace("#" + ColName + "#", TempRow[ColName].ToString());
            }
            completeHTML = completeHTML + ReplaceHTMLTemplate;
        }
        return completeHTML;
    }


    internal static void SendEmail()
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Increments a SessionCounter which must be SET and Retrieved inside the controller, but incrementing is a bit confusing. This can ALSO effectively initialize the SessionCounter the first time by returning 1 for a null counter
    /// </summary>
    /// <param name="sessioncounter"></param>
    /// <returns></returns>
    public static String IncrementSessionCounter(Object sessioncounter)
    {
        if (sessioncounter == null)
        {
            return "1";
        }
        else
        {
            return (Int32.Parse(sessioncounter.ToString()) + 1).ToString();
        }
    }

}